#include "ImaActiveUsers.h"
#include "ImaLogger.h"
#include "ImaServiceConstants.h"

extern ImaLogger* gpLogger;

ImaActiveUsers* ImaActiveUsers::mpInstance = NULL;

ImaActiveUsers* ImaActiveUsers::getInstance() {
	LOG(debug, __FILE__, __LINE__, "ImaActiveUsers::getInstance() invoked");

	if(mpInstance == NULL) { //TODO: Apply DCLP here for multithreaded use
		mpInstance = new ImaActiveUsers();
		LOG(info, __FILE__, __LINE__, "ImaActiveUsers instance created");
	}

	return mpInstance;
}

ImaActiveUsers::ImaActiveUsers() {
	LOG(debug, __FILE__, __LINE__, "ImaActiveUsers::ImaActiveUsers()");
}

ImaActiveUsers::~ImaActiveUsers() {
	LOG(debug, __FILE__, __LINE__, "ImaActiveUsers::~ImaActiveUsers()");

	//TODO: Iterate through the session map and remove and destroy all objects
}

bool ImaActiveUsers::isUserActive(const std::string& emailId, const std::string& tokenId) {
	char log_msg[1024];
	sprintf(log_msg, "isUserActive called with email id: %s", emailId.c_str());
	LOG(debug, __FILE__, __LINE__, log_msg);

	bool bRetVal = false;

	SessionMap::iterator it;
	it = mSessionMap.find(emailId);
	if(it != mSessionMap.end())  {
		USER_SESSION_INFO* pInfo = it->second;
		if(pInfo->tokenId.compare(tokenId) == 0) {
			time_t currTime = time(NULL);
			if(difftime(currTime, pInfo->lastAccessTime) > USER_SESSION_TIMEOUT) {
				//remove this user from the map and return false
				mSessionMap.erase(it);
				delete pInfo;
				bRetVal = false;
				LOG(debug, __FILE__, __LINE__, "The user was inactive for long");
			} else {
				pInfo->lastAccessTime = currTime;
				bRetVal = true;
				LOG(debug, __FILE__, __LINE__, "The user is active");
			}
		}
	}

	return bRetVal;	
}

int ImaActiveUsers::insertUser(const std::string& emailId, const std::string& tokenId){
	char log_msg[1024];
	sprintf(log_msg, "insertUser called with emailId: %s and tokenId: %s", emailId.c_str(), tokenId.c_str());
	LOG(debug, __FILE__, __LINE__, log_msg);

	SessionMap::iterator it;
	it = mSessionMap.find(emailId);
	if(it != mSessionMap.end()) {
		USER_SESSION_INFO* pInfo = it->second;
		pInfo->tokenId = tokenId;
		pInfo->lastAccessTime = time(NULL);
		LOG(debug, __FILE__, __LINE__, "User info was already there in the map. Updated the same.");
	} else {
		USER_SESSION_INFO* pInfo = new USER_SESSION_INFO;
		pInfo->tokenId = tokenId;
		pInfo->lastAccessTime = time(NULL);
		LOG(debug, __FILE__, __LINE__, "User info inserted into the map");
		mSessionMap.insert(std::pair<std::string, USER_SESSION_INFO*>(emailId, pInfo));
	}

	return 0;	
}


bool ImaActiveUsers::removeUser(const std::string& emailId) {
	char log_msg[1024];
	sprintf(log_msg, "removeUser called with user id: %s", emailId.c_str());
	LOG(debug, __FILE__, __LINE__, log_msg);

	bool bRetVal = true;

	SessionMap::iterator it;
	it = mSessionMap.find(emailId);
	if(it != mSessionMap.end()) {
		USER_SESSION_INFO* pInfo = it->second;
		mSessionMap.erase(it);
		delete pInfo;
		sprintf(log_msg, "User %s removed from the active users map", emailId.c_str());
		LOG(debug, __FILE__, __LINE__, log_msg);
	} else {
		sprintf(log_msg, "User %s not found in the active users map", emailId.c_str());
		LOG(debug, __FILE__, __LINE__, log_msg);
	}

	return bRetVal;
}

bool ImaActiveUsers::removeUser(const std::string& emailId, const std::string& strTokenId) {
	char log_msg[1024];
	sprintf(log_msg, "removeUser called with email id: %s", emailId.c_str());
	LOG(debug, __FILE__, __LINE__, log_msg);

	bool bRetVal = true;

	SessionMap::iterator it;
	it = mSessionMap.find(emailId);
	if(it != mSessionMap.end()) {
		USER_SESSION_INFO* pInfo = it->second;
		if(strTokenId.compare(pInfo->tokenId) == 0) {
			sprintf(log_msg, "User %s  removed from the active users map", emailId.c_str());
			LOG(debug, __FILE__, __LINE__, log_msg);
			mSessionMap.erase(it);
			delete pInfo;
		} else {
			bRetVal = false;
			LOG(debug, __FILE__, __LINE__, "Token Id didnt match");
		}
	} else {
		sprintf(log_msg, "User %s not found in the active users map", emailId.c_str());
		LOG(debug, __FILE__, __LINE__, log_msg);
		bRetVal = false;
	}

	return bRetVal;
}

//TODO: This would be invoked via a separate thread that would run periodically
int ImaActiveUsers::removeTimedoutUSers() {
	LOG(debug, __FILE__, __LINE__, "removeTimedoutUSers called");

	unsigned int numRemoved = 0;

	std::map<std::string, USER_SESSION_INFO*>::iterator it = mSessionMap.begin();
	time_t currTime = time(NULL);
	while(it != mSessionMap.end()) {
		USER_SESSION_INFO* pInfo = it->second;
		if(difftime(currTime, pInfo->lastAccessTime) > USER_SESSION_TIMEOUT) {
			mSessionMap.erase(it++);
			delete pInfo;
			numRemoved++;
		} else {
			++it;
		}
	}

	char log_msg[1024];
	sprintf(log_msg, "%u users removed from map after timeout", numRemoved);
	LOG(debug, __FILE__, __LINE__, log_msg);

	return numRemoved;
}

